package com.tzh.plugin.utils;

import com.tzh.utils.ResultDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class ControllerUtils {

    /**
     * Controller注解
     *
     * @param cls
     * @param classObjName
     */
    public static void addRestAnnotation(TopLevelClass cls, String classObjName) {
        cls.addAnnotation("@RestController");
        cls.addAnnotation("@RequestMapping(\"/" + classObjName + "\")");
    }

    /**
     * 接口引包
     *
     * @param cls
     */
    public static void addRestImportTypes(TopLevelClass cls) {
        FullyQualifiedJavaType controllerType = new FullyQualifiedJavaType(RestController.class.getName());
        FullyQualifiedJavaType requestMappingType = new FullyQualifiedJavaType(RequestMapping.class.getName());
        FullyQualifiedJavaType getMappingType = new FullyQualifiedJavaType(GetMapping.class.getName());
        FullyQualifiedJavaType postMappingType = new FullyQualifiedJavaType(PostMapping.class.getName());
        FullyQualifiedJavaType listType = new FullyQualifiedJavaType(List.class.getName());
        FullyQualifiedJavaType resultType = new FullyQualifiedJavaType(ResultDto.class.getName());
        FullyQualifiedJavaType requestType = new FullyQualifiedJavaType(RequestBody.class.getName());
        FullyQualifiedJavaType apiType = new FullyQualifiedJavaType(Api.class.getName());
        FullyQualifiedJavaType apiOpType = new FullyQualifiedJavaType(ApiOperation.class.getName());
        FullyQualifiedJavaType autowiedType = new FullyQualifiedJavaType(Autowired.class.getName());
        FullyQualifiedJavaType apiparam = new FullyQualifiedJavaType(ApiImplicitParam.class.getName());
        FullyQualifiedJavaType apiparams = new FullyQualifiedJavaType(ApiImplicitParams.class.getName());
        FullyQualifiedJavaType pathVariableType = new FullyQualifiedJavaType(PathVariable.class.getName());
        FullyQualifiedJavaType validType = new FullyQualifiedJavaType(Validated.class.getName());
        cls.addImportedType(validType);
        cls.addImportedType(apiparam);
        cls.addImportedType(apiparams);
        cls.addImportedType(controllerType);
        cls.addImportedType(requestMappingType);
        cls.addImportedType(getMappingType);
        cls.addImportedType(postMappingType);
        cls.addImportedType(listType);
        cls.addImportedType(resultType);
        cls.addImportedType(requestType);
        cls.addImportedType(apiType);
        cls.addImportedType(apiOpType);
        cls.addImportedType(autowiedType);
        cls.addImportedType(pathVariableType);
    }

    public static void addMethods(TopLevelClass cls, IntrospectedTable introspectedTable, Map<String, FullyQualifiedJavaType> dtoData) {
        CreateMethod("query", "listRet", cls, introspectedTable, dtoData);
        CreateMethod("add", "model", cls, introspectedTable, dtoData);
        CreateMethod("del", "int", cls, introspectedTable, dtoData);
        CreateMethod("update", "int", cls, introspectedTable, dtoData);
        CreateMethod("delById", "int", cls, introspectedTable, dtoData);
        CreateMethod("selectById", "detail", cls, introspectedTable, dtoData);
    }

    private static void CreateMethod(String paramStr, String retStr, TopLevelClass cls, IntrospectedTable introspectedTable, Map<String, FullyQualifiedJavaType> dtoData) {

        String modelName = dtoData.get("model").getShortName();
        String methodName = paramStr.replace("ById", "") + modelName;
        FullyQualifiedJavaType returnType = dtoData.get(retStr);
        FullyQualifiedJavaType paramterType = null;
        String paramName = null;
        Method method = null;
        if (paramStr.contains("ById")) {
            if ("detail".equals(retStr)) {
                methodName += "Detail";
                //处理service
                dtoData.put(paramStr + "detail", returnType);
            }
            method = new Method(methodName);
            List<IntrospectedColumn> primaryKeys = introspectedTable.getPrimaryKeyColumns();
            Method finalMethod = method;
            StringBuilder path = new StringBuilder();
            if (primaryKeys != null && primaryKeys.size() > 0) {
                method.addAnnotation("@ApiImplicitParams({");
                primaryKeys.stream().forEach(col -> {
                    System.out.println(col.getActualColumnName());
                    String colName = col.getJavaProperty();
                    FullyQualifiedJavaType paramType = col.getFullyQualifiedJavaType();
                    Parameter parameter = new Parameter(paramType, colName);
                    finalMethod.addParameter(parameter);
                    parameter.addAnnotation("@PathVariable(\"" + colName + "\")");
                    path.append("/{" + colName + "}");
                    Properties prop = introspectedTable.getTableConfiguration().getProperties();
                    String comment = null;
                    if (prop != null) {
                        String colStr = prop.getProperty(introspectedTable.getFullyQualifiedTableNameAtRuntime() + ":" + col.getActualColumnName());
                        if (!StringUtils.isEmpty(colStr)) {
                            comment = colStr.split(":", 3)[2];
                        }
                    }
                    //联合主键增加，
                    List<String> list = finalMethod.getAnnotations();
                    List<String> annList = new ArrayList<String>();
                    annList.addAll(list);
                    for (String ann : annList) {
                        if (ann.contains("@ApiImplicitParam(")) {
                            finalMethod.addAnnotation(",");
                            break;
                        }
                    }
                    finalMethod.addAnnotation("@ApiImplicitParam(name = \"" + colName + "\" ,required = true,value = \"" + (!StringUtils.isEmpty(comment) && !"null".equals(comment) ? comment : "主键") + "\",dataType = \"" + paramType.getShortName() + "\",paramType =\"path\")");
                });
                method.addAnnotation("})");
            }

            method.addAnnotation("@GetMapping(value=\"" + methodName.toLowerCase() + path.toString() + "\" ,produces={\"application/json;charset=UTF-8\"})");
        } else {
            paramterType = dtoData.get(paramStr);
            paramName = paramterType.getShortName().toLowerCase();
            Parameter parameter = new Parameter(paramterType, paramName);
            method = new Method(methodName);
            method.addParameter(parameter);
            if (paramStr.contains("add") || paramStr.contains("update")) {
                parameter.addAnnotation("@RequestBody @Validated");
                method.addAnnotation("@PostMapping(value=\"" + paramStr.toLowerCase() + "\" ,produces={\"application/json;charset=UTF-8\"},consumes=\"application/json\")");
            } else {
                method.addAnnotation("@GetMapping(value=\"" + paramStr.toLowerCase() + "\" ,produces={\"application/json;charset=UTF-8\"})");
            }
        }
        FullyQualifiedJavaType resultType = new FullyQualifiedJavaType(ResultDto.class.getName());
        resultType.addTypeArgument(returnType);
        method.setReturnType(resultType);//returnType.getShortName()
        method.setVisibility(JavaVisibility.PUBLIC);
        addBody(method, paramStr, retStr, cls, dtoData);
        method.addAnnotation("@ApiOperation(\"" + GenerateUtils.apiDesc.get(paramStr) + "\")");


        cls.addMethod(method);
    }

    private static void addBody(Method method, String paramStr, String retStr, TopLevelClass cls, Map<String, FullyQualifiedJavaType> dtoData) {
        List<Field> fields = cls.getFields();
        String className = dtoData.get("model").getShortName();
        FullyQualifiedJavaType retType = dtoData.get(retStr);
        fields.stream().forEach(field -> {
            String fieldName = field.getName();
            if (fieldName.contains("Service") && (fieldName.contains(className))) {
                FullyQualifiedJavaType paraType = dtoData.get(paramStr);
                String paramName = null;
                String serivceMethodName = null;
                if ("query".equals(paramStr)) {
                    serivceMethodName = paramStr + className + "List";
                    paramName = paraType.getShortName().toLowerCase();
                    method.addBodyLine(retType.getShortName() + " result = " + fieldName + "." + serivceMethodName + "(" + paramName + ");");
                    method.addBodyLine("return ResultDto.setSuccess(result);");
                } else {
                    if (paramStr.contains("ById")) {
                        List<Parameter> params = method.getParameters();
                        for (int i = 0; i < params.size(); i++) {
                            if (i == 0) {
                                paramName = params.get(i).getName();
                            } else {
                                paramName += "," + params.get(i).getName();
                            }

                        }

                        if (dtoData.containsKey(paramStr + "detail")) {
                            serivceMethodName = paramStr.replace("ById", "") + className + "Detail";
                        } else {
                            serivceMethodName = paramStr.replace("ById", "") + className;
                        }
                        method.addBodyLine(retType.getShortName() + " result = " + fieldName + "." + serivceMethodName + "(" + paramName + ");");
                        method.addBodyLine("return ResultDto.setSuccess(result);");

                    } else {
                        serivceMethodName = paramStr + className;
                        paramName = paraType.getShortName().toLowerCase();

                        method.addBodyLine(retType.getShortName() + " result = " + fieldName + "." + serivceMethodName + "(" + paramName + ");");
                        method.addBodyLine("return ResultDto.setSuccess(result);");
                    }
                }
            }
        });
    }

    public static void addField(TopLevelClass cls, Map<String, FullyQualifiedJavaType> dtoData, String className) {

        String path = cls.getType().getFullyQualifiedName().replace("controller." + className + "Controller", "");
        FullyQualifiedJavaType interfaceType = new FullyQualifiedJavaType(path + "service.I" + className + "Service");
        cls.addImportedType(interfaceType);
        dtoData.put("service", interfaceType);
        String serviceName = interfaceType.getShortName();
        char[] serviceChar = serviceName.toCharArray();
        serviceChar[0] += 32;
        Field field = new Field(String.valueOf(serviceChar), interfaceType);
        field.setVisibility(JavaVisibility.PRIVATE);
        field.addAnnotation("@Autowired");
        cls.addField(field);
    }

}
